/*****************************************************************************
 * $LastChangedDate: 2012-10-25 21:35:57 -0400 (Thu, 25 Oct 2012) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Mouse device class (OSG).
 * @remarks OSG will confuse views and mouse events if every osg::View
 *          is allowed to be the focus (the receiver) of events.
 *          Rather, only allow the main view to receive events using:
 *          osg::Camera->setAllowEventFocus( ifMainView );
 *//*
 * LEGAL:   COPYRIGHT (C) 2009 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#if COMPILE_OSG

#define INPUT_DEVICE_MOUSE_OSG_CC 1
#if VERBOSE
#define MOUSE_EVENTS_VERBOSE 1
#endif
#include "base/module.hh"
#include "base/clamp.hh"
#include "base/stream.hh"
using namespace base;
#include "gfx/module.hh"
#include "gfx/osg.hh"
using namespace gfx;
#include "view/module.hh"
#include "view/window.hh"  // to register event handler
using namespace view;
#include "input/module.hh"
#include "input/device_mouse.hh"
#include "input/device_mouse_osg.hh"

#include <osgGA/GUIEventAdapter>

namespace input {

////////////////////////////////////////////////////////////////////////////////
////////////////////////////  MouseDeviceOSG  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
MouseDeviceOSG::MouseDeviceOSG( void )
{
ASSERT_RUN_ONCE;
    // NOP
}

MouseDeviceOSG::~MouseDeviceOSG()
{
    // NOP
}

/*****************************************************************************
 * Enable mouse device.
 *****************************************************************************/
bool
MouseDeviceOSG::EnableDevice( void )
{
BEGIN_ONCE {  // base Device class has logic to call EnableDevice() only once

    // Register handler with OSG Viewer which can be obtained
    // from the Window singleton (in view module).
    GET_WINDOW().OsgRegisterEventHandler( new MouseDeviceOSG::HandlerOSG );

} END_ONCE

    return true;
}

/*****************************************************************************
 * Handles mouse events sent from OSG.
 *****************************************************************************/
bool
MouseDeviceOSG::HandlerOSG::handle( const osgGA::GUIEventAdapter& eventOSG, osgGA::GUIActionAdapter& )
{
    // Note: OSG events are indicated by bits.

    if ( eventOSG.getEventType() & osgGA::GUIEventAdapter::MOVE )
    {
        // osg/GUIEventAdapter:
        // "return the getX() value normalized to the range of -1 to 1."
        // "-1 would be the left hand side of the window."
        // "0.0 would be the middle of the window."
        // "+1 would be the right hand side of the window."
        fp x = eventOSG.getXnormalized();
        fp y = eventOSG.getYnormalized();
        const WidthHeight<int> wh = GET_WINDOW().GetWindowSize();
        x = (x + 1.0f) * 0.5f;  // {-1..+1} --> {0..1}
        y = (y + 1.0f) * 0.5f;
        x = Clamp1( x );
        y = Clamp1( y );
        x *= wh.w;
        y *= wh.h;

        // Translate OSG event to MouseEvent struct (don't confuse event structs).
        const MouseEvent mouseEvent( MouseEvent::eChange_MOVEMENT,         // what changed
                                     Vector2( x, y ),                      // mouse position
                                     MouseEvent::INVALID_BUTTON, false );  // button N/A

        // Enqueue event.
        GET_MOUSE_DEVICE().Enqueue( mouseEvent );

#if MOUSE_EVENTS_VERBOSE
        CLOG << "Enqueueing movement mouse event: pos=" << mouseEvent.mPos << std::endl;
#endif

        return true;
    }
    else if ( (eventOSG.getEventType() & osgGA::GUIEventAdapter::RELEASE)
           or (eventOSG.getEventType() & osgGA::GUIEventAdapter::PUSH) )
    {
        // Translate OSG event to MouseEvent struct (don't confuse event structs).
        const int buttonNum = eventOSG.getButton();
        if ( buttonNum != -1 )
        {
            const bool buttonState = BOOL_CAST( eventOSG.getEventType() & osgGA::GUIEventAdapter::PUSH );
            const MouseEvent mouseEvent( MouseEvent::eChange_MOVEMENT,  // what changed
                                         Vector2(0,0),                  // mouse position N/A
                                         buttonNum, buttonState );      // button, state

            // Enqueue event.
            GET_MOUSE_DEVICE().Enqueue( mouseEvent );

#if MOUSE_EVENTS_VERBOSE
        CLOG << "Enqueueing button mouse event: buttonNum=" << mousEvent.mButtonNum << " buttonState=" << mousEvent.mButtonState << std::endl;
#endif
            return true;
        }
        else
        {
            // Invalid button number (unexpected).
            CERROR << "WARNING[MouseDeviceOSG]: invalid mouse buttion (-1)" << std::endl;
            return false;
        }
    }
    else
    {
        // Not handled.
        return false;
    }
}

#if OSG_VERSION_LESS_THAN(3,0,0)
void
MouseDeviceOSG::HandlerOSG::accept( osgGA::GUIEventHandlerVisitor& v )
{
    v.visit( *this );
}
#endif

} // namespace input

#endif // COMPILE_OSG
